#!/usr/bin/env python

"""Test show how is elected a leader in a n-cycle graph.
Usage: python leader_election.py [n]"""

import sys

import logging
import networkx as nx
from networkx.classes.function import neighbors
import nxchaos as nxc

#_______________________________________________________________________________

NODES = 10
nxc.setup_log('debug')
_logger = logging.getLogger('nxchaos.test')




class Parrent:
    '''Helping class containg data about a nodes parrent'''
    color = -1
    id = 999999 # Don't exists
    port = 999999 # Unreachable
    

#_______________________________________________________________________________
class ThreeCVColoring(nxc.algorithm.IAlgorithm):

    
    
    def orientation(self):
        '''
        Orients the tree. Every node will set its parrent and childs
        according to the data received from the input
        Parrent is the vertex with the lowest id from the neighbours
        '''
        # Let the first vertex be the root.
        if(self.id == 1):
            self.root = True
            self.parrent.color = 0
            self.parrent.id = None # Don't exists
            self.parrent.port = None # Unreachable
        else:
            if len(self.inbox) > 0:
                for port, neighbour_id in self.inbox:
                    self.neighbours += 1
                    #print "V:", self.id, "CUR:",  self.parrent.id, self.parrent.port, 'swap', message, port
                    if neighbour_id < self.parrent.id:
                        self.parrent.id = neighbour_id
                        self.parrent.port = port

        return

    
    def send_to_childs(self, message):
        '''Sends a message Only to the childs'''
        for i in range(self.neighbours):
            if i!=self.parrent.port:
                self.send(i, message)
    
    def join_colors(self, a, b):
        '''procedure joins the colors like in the C&V algorithm
            returns a new color (int)'''
        i=0
        while i<len(a) and i<len(b) and a[-i]==b[-i]:
            i+=1
        color_string = bin(i)[2:]+str(a[-i]) # Join the colors
        color_int = int(color_string, 2) # Change to int
        return color_int

    
    def run(self):
        self.root = False
        self.parrent = Parrent()
        self.color = self.id
        self.neighbours = 0

        # Introducing itself to the neighbours
        self.send(0, self.id)
        self.send(1, self.id)
        self.send(2, self.id)
        yield # End of Round 0
            
        # Orienting in the tree
        self.orientation()
        _logger.info("[{0}] is a child of {1}".format(self.id, self.parrent.id))
        yield # End of Round 1


        while(True):
            # Sending the color to childs
            self.send_to_childs(self.color)
            yield

            for port, color in self.inbox:
                self.parrent.color = color

            # Now the main algorithm
            # Coloring the tree with 6 colors
            color_bin = bin(self.color)[2:]
            parent_color_bin = bin(self.parrent.color)[2:]
            new_color = self.join_colors(color_bin, parent_color_bin)
           # print "I:", self.id, "C:", self.color, "PC:", self.parrent.color, "NC:", new_color
            if new_color==self.color:
                _logger.info("[{id}] color is: {color}".format(id=self.id, color=self.color))
                self.save_data(["color", self.color])
                return #END!
            else:
                self.color = new_color
            yield

            
            

#_______________________________________________________________________________

if __name__ == "__main__":

    if len(sys.argv) > 1:
        NODES = int(sys.argv[1])

    _logger.info("Nodes in a graph: {0}".format(NODES))

    gfactory = nxc.graph.GraphFactory()
    graph = gfactory.from_builtin("btree", n=NODES)


    _logger.info("Rendering graph.")
    nxc.util.render_picture(graph, True, True, 'btree.png')

    network = nxc.network.Network(graph, ThreeCVColoring)
    network.save_partial_data(True, './3cv_partial_data.txt')
    network.prepare()

    while network.is_working():
        _logger.info("Round nr {0}".format(network.round_number()))
        network.round()
        



